<?php

class base_shell_buildin extends base_shell_prototype {

    var $vars;

    var $command_alias = array(
        'ls' => 'list',
        'q' => 'exit',
        'quit' => 'exit'
    );

    function php_call() {
        if ($this->vars)
            extract($this->vars);
        $this->output(eval(func_get_arg(0)));
        $this->vars = get_defined_vars();
    }

    function command_reset() {
        $this->cmdlibs = null;
        $this->vars = null;
    }

    var $command_help_options = array(
        'verbose' => array(
            'title' => '显示详细信息',
            'short' => 'v'
        )
    );

    function command_help($help_item = null, $shell_command = null) {
        if ($help_item) {
            list ($app_id, $package) = explode(':', $help_item);
            $this->app_help($app_id, $package, $shell_command);
        } else {
            $this->help();
            $this->output_line(app::get('base')->_('应用提供的命令：'));
            if ($handle = opendir(APP_DIR)) {
                while (false !== ($app_id = readdir($handle))) {
                    if ($app_id{0} != '.' && is_dir(APP_DIR . '/' . $app_id) && is_dir(APP_DIR . '/' . $app_id . '/lib/command')) {
                        $this->app_help($app_id);
                    }
                }
                closedir($handle);
            }
            $this->output_line(app::get('base')->_('原生php命令'));
            $____params = app::get('base')->_('输入命令如果以分号[;]结尾，则被认为是一条php语句.  例如:');
            echo <<<EOF
$____params
  1> \$a = 2;
     int(2)
  2> pow(\$a,8);
     int(256)

EOF;
        }
    }

    function app_help($app_id, $package = null, $command = false) {
        if ($package) {
            $commander = $this->shell->get_commander($app_id, $package);
            $commander->help($command);
        } else {
            if ($handle = opendir(APP_DIR . '/' . $app_id . '/lib/command')) {
                while (false !== ($file = readdir($handle))) {
                    if (substr($file, - 4, 4) == '.php' && is_file(APP_DIR . '/' . $app_id . '/lib/command/' . $file)) {
                        $commander = $this->shell->get_commander($app_id, substr($file, 0, - 4));
                        if ($commander) {
                            $commander->help();
                        }
                    }
                }
                closedir($handle);
            }
        }
    }

    function name_prefix() {
        return '';
    }

    var $command_exit = '退出';

    function command_exit() {
        echo 'exit';
        exit();
    }

    var $command_man = '显示帮助';

    function command_man() {
        $args = func_get_args();
        foreach ($args as $arg) {
            kernel::single('base_misc_man')->show($arg);
        }
    }

    var $command_sh = '执行操作系统命令';

    function command_sh($args) {
        eval('system("' . str_replace('"', '\\"', implode(' ', $args)) . '");');
    }

    var $command_mkconfig = '创建config文件';

    var $command_mkconfig_options = array(
        'dbhost' => array(
            'title' => '数据库服务器，默认:localhost',
            'short' => 'h',
            'need_value' => 1
        ),
        'dbpassword' => array(
            'title' => '数据库密码',
            'short' => 'p',
            'need_value' => 1
        ),
        'dbuser' => array(
            'title' => '数据库用户名',
            'short' => 'u',
            'need_value' => 1
        ),
        'dbname' => array(
            'title' => '数据库名',
            'short' => 'n',
            'need_value' => 1
        ),
        'dbprefix' => array(
            'title' => '数据库前缀, 默认sdb_',
            'short' => 'a',
            'need_value' => 1
        ),
        'timezone' => array(
            'title' => '时区',
            'short' => 't',
            'need_value' => 1
        )
    );

    function command_mkconfig() {
        $options = $this->get_options();
        $options = array(
            'db_host' => $options['dbhost'] ? $options['dbhost'] : 'localhost',
            'db_user' => $options['dbuser'] ? $options['dbuser'] : 'root',
            'db_password' => $options['dbpassword'],
            'db_name' => $options['dbname'],
            'db_prefix' => $options['dbprefix'] ? $options['dbprefix'] : 'sdb_',
            'default_timezone' => $options['timezone']
        );
        kernel::single('base_setup_config')->write($options);
    }

    var $command_ls = '列出所有应用';

    function command_ls() {
        $rows = app::get('base')->model('apps')->getlist('*', array(
            'installed' => true
        ));
        foreach ($rows as $k => $v) {
            $rows[$k] = array(
                'app_id' => $v['app_id'],
                'name' => $v['name'],
                'version' => $v['version'],
                'status' => $v['status'] ? $v['status'] : 'uninstalled'
            );
        }
        $this->output_table($rows);
    }

    var $command_cd = '切换当前应用';

    function command_cd($app = '') {
        if ($app == '..') {
            $app = '';
        } elseif ($app == '-') {
            $app = $this->last_app_id;
        }
        
        if ($app{0} != '.' && is_dir(APP_DIR . '/' . $app)) {
            $this->last_app_id = $this->shell->app_id;
            $this->shell->app_id = $app;
        } else {
            throw new Exception($app . ": No such application.\n");
        }
    }

    var $command_install = '安装应用';

    var $command_install_options = array(
        'reset' => array(
            'title' => '重新安装',
            'short' => 'r'
        ),
        'options' => array(
            'title' => '参数',
            'short' => 'o',
            'need_value' => 1
        )
    );

    function command_install() {
        $args = func_get_args();
        $options = $this->get_options();
        $install_queue = kernel::single('base_application_manage')->install_queue($args, $options['reset']);
        $this->install_app_by_install_queue($install_queue, $options);
    }

    private function install_app_by_install_queue($install_queue, $options = array()) {
        if (! $install_queue) {
            return;
        }
        
        if (kernel::single('base_application_manage')->has_conflict_apps(array_keys($install_queue), $conflict_info)) {
            foreach ($conflict_info as $conflict_app_id => $conflict_detail) {
                $conflict_app_info = app::get($conflict_app_id)->define();
                $conflict_message .= $conflict_app_info['name'] . ' ' . app::get('base')->_('与') . ' ' . $conflict_detail['name'] . ' ' . app::get('base')->_('存在冲突') . "\n";
            }
            logger::info($conflict_message . app::get('base')->_('请手工卸载冲突应用') . "\n");
            return false;
        } // todo：安装时判断app冲突，检测包括所有依赖的app和现有安装app之间的冲突
        
        if ($options['options']) {
            parse_str($options['options'], $this->shell->input);
        }
        // ---start---增加如果是通过web访问，则进行相关处理-------@lujunyi------
        if ($_SERVER['HTTP_USER_AGENT'] && ! $this->shell->input) {
            $a = 'install ' . $args[0] . ' -o "';
            foreach ((array) $install_queue as $app_id_ => $app_info_) {
                if (! $app_info_) {
                    logger::info(app::get('base')->_('无法找到应用') . $app_id_ . "\n");
                    return false;
                }
                $install_options_ = app::get($app_id_)->runtask('install_options');
                if ($install_options_) {
                    $c = '';
                    foreach ($install_options_ as $key => $item) {
                        $b .= $app_id_ . '[' . $key . ']=' . $item['default'] . '&';
                    }
                }
                $c .= $b;
            }
            $d = rtrim($c, '&');
            if (! empty($d)) {
                echo "请按以下格式输入数据：\n";
                $e = $a . $d . '"';
                echo $e;
                exit();
            }
            ;
        }
        // ---end---
        foreach ((array) $install_queue as $app_id => $app_info) {
            if (! $app_info) {
                logger::info(app::get('base')->_('无法找到应用') . $app_id . "\n");
                return false;
            }
            if (! kernel::single('base_setup_lock')->lockfile_exists()) {
                kernel::single('base_setup_lock')->write_lock_file(false);
            } // todo: 生成setup lock文件
            $install_options = app::get($app_id)->runtask('install_options');
            
            if (is_array($install_options) && count($install_options) > 0 && ! $this->shell->input[$app_id]) {
                do {
                    $this->shell->input_option($install_options, $app_id);
                } while (app::get($app_id)->runtask('checkenv', $this->shell->input[$app_id]) === false);
            }
            kernel::single('base_application_manage')->install($app_id, $this->shell->input[$app_id]);
        }
    }

    var $command_install_product = "安装产品";

    var $command_install_product_options = array(
        'reset' => array(
            'title' => '重新安装',
            'short' => 'r'
        ),
        'options' => array(
            'title' => '参数',
            'short' => 'o',
            'need_value' => 1
        )
    );

    function command_install_product() {
        $args = func_get_args();
        $options = $this->get_options();
        $config = base_setup_config::deploy_info();
        // if (!$args[0] || $args[0] != $config['product_name']) echo app::get('base')->_('产品名称无法找到！');
        
        foreach ($config['package']['app'] as $k => $app) {
            if ($app['default'] === 'true') {
                $applist[] = $app['id'];
            }
        }
        
        $applist = kernel::single('base_application_manage')->install_queue($applist);
        $this->install_app_by_install_queue($applist, $options);
    }

    var $command_uninstall = '卸载应用';

    var $command_uninstall_options = array(
        'recursive' => array(
            'title' => '递归删除依赖之app',
            'short' => 'r'
        )
    );

    function command_uninstall() {
        $args = func_get_args();
        $uninstall_queue = kernel::single('base_application_manage')->uninstall_queue($args);
        $options = $this->get_options();
        
        if (! $options['recursive']) {
            foreach ($uninstall_queue as $app_id => $type) {
                $to_delete[$type[1]][] = $app_id;
            }
            if ($to_delete[1]) {
                echo 'error in remove app ' . implode(' ', $args) . "\n";
                echo app::get('base')->_("以下应用依赖欲删除的app: ") . implode(' ', $to_delete[1]) . "\n";
                echo app::get('base')->_("使用 -r 参数按依赖关系全部删除");
                return true;
            }
        }
        foreach ($uninstall_queue as $app_id => $type) {
            kernel::single('base_application_manage')->uninstall($app_id);
        }
    }

    var $command_pause = '暂停应用';

    var $command_pause_options = array(
        'recursive' => array(
            'title' => '递归删除依赖之app',
            'short' => 'r'
        )
    );

    function command_pause() {
        $args = func_get_args();
        $pause_queue = kernel::single('base_application_manage')->pause_queue($args);
        $options = $this->get_options();
        
        if (! $options['recursive']) {
            foreach ($pause_queue as $app_id => $type) {
                $to_pause[$type[1]][] = $app_id;
            }
            if ($to_pause[1]) {
                echo 'error in pause app ' . implode(' ', $args) . "\n";
                echo app::get('base')->_("以下应用依赖欲暂停的app: ") . implode(' ', $to_pause[1]) . "\n";
                echo app::get('base')->_("使用 -r 参数按依赖关系全部暂停");
                return true;
            }
        }
        foreach ($pause_queue as $app_id => $type) {
            kernel::single('base_application_manage')->pause($app_id);
        }
    }

    // End Function
    var $command_active = '开启应用';

    var $command_active_options = array(
        'recursive' => array(
            'title' => '递归删除依赖之app',
            'short' => 'r'
        )
    );

    function command_active() {
        $args = func_get_args();
        $active_queue = kernel::single('base_application_manage')->active_queue($args);
        $options = $this->get_options();
        if (! $active_queue) {
            return;
        }
        
        if (kernel::single('base_application_manage')->has_conflict_apps(array_keys($active_queue), $conflict_info)) {
            foreach ($conflict_info as $conflict_app_id => $conflict_detail) {
                $conflict_app_info = app::get($conflict_app_id)->define();
                $conflict_message .= $conflict_app_info['name'] . ' ' . app::get('base')->_('与') . ' ' . $conflict_detail['name'] . ' ' . app::get('base')->_('存在冲突') . "\n";
            }
            logger::info($conflict_message . app::get('base')->_('请手工卸载冲突应用') . "\n");
            return false;
        } // todo：安装时判断app冲突，检测包括所有依赖的app和现有安装app之间的冲突
        
        foreach ((array) $active_queue as $app_id => $app_info) {
            if (! $app_info) {
                logger::info(app::get('base')->_('无法找到应用') . $app_id . "\n");
                return false;
            }
            kernel::single('base_application_manage')->active($app_id);
        }
    }

    // End Function
    var $command_update = '升级应用程序';

    var $command_update_options = array(
        'sync' => array(
            'title' => '升级应用程序信息库'
        ),
        'sync-only' => array(
            'title' => '仅升级应用程序信息库'
        ),
        'force-download' => array(
            'title' => '强制下载'
        ),
        'download-only' => array(
            'title' => '仅下载应用'
        ),
        'ignore-download' => array(
            'title' => '忽略下载'
        ),
        'custom-force-update-db' => array(
            'title' => 'custom首次安装强制更新数据库'
        ),
        'force-update-db' => array(
            'title' => '强制更新数据库'
        ),
        'force-update-app' => array(
            'title' => '强制更新应用程序'
        )
    );

    function command_update() {
        if (defined('BASE_CACHE_EXPIRES')) {
            // 把base_cache_expire 同步到kv中
            $rows = kernel::database()->select('SELECT UPPER(`type`) AS `type`, UPPER(`name`) AS `name`, `expire` FROM sdb_base_cache_expires', true);
            foreach ($rows as $row) {
                $result = array(
                    'expire' => time(),
                    'type' => $row['type'],
                    'name' => $row['name'],
                    'app' => $row['app']
                );
                base_kvstore::instance('cache/cache_expires')->store($row['name'], $result);
            }
        }
        $options = $this->get_options();
        if ($options['sync'] || $options['sync-only']) {
            kernel::single('base_application_manage')->sync();
        } else {
            kernel::single('base_application_manage')->update_local();
        }
        
        if ($options['sync-only']) {
            return true;
        }
        
        if (! $args) {
            $rows = app::get('base')->model('apps')->getList('app_id', array(
                'installed' => 1
            ));
            foreach ($rows as $r) {
                if ($r['app_id'] == 'base')
                    continue;
                $args[] = $r['app_id'];
            }
        }
        array_unshift($args, 'base'); // todo:总是需要先更新base
        $args = array_unique($args);
        
        if (! $options['ignore-download']) {
            foreach ($args as $app_id) {
                // kernel::single('base_application_manage')->download($app_id,$options['force-download']); //todo:临时去掉
            }
        }
        
        if ($options['force-update-db']) {
            foreach (utils::tree(ROOT_DIR . '/app') as $k => $v) {
                if (strstr($v, 'dbschema') && is_file($v)) {
                    touch($v);
                }
                base_application_dbtable::$force_update = true;
            }
        }
        
        if ($options['custom-force-update-db']) {
            
            if (defined('CUSTOM_CORE_DIR')) {
                foreach (utils::tree(CUSTOM_CORE_DIR) as $k => $v) {
                    if (is_file($v)) {
                        touch($v);
                    }
                }
            }
        }
        
        if (! $options['download-only']) {
            foreach ($args as $app_id) {
                $appinfo = app::get('base')->model('apps')->getList('*', array(
                    'app_id' => $app_id
                ));
                if (version_compare($appinfo[0]['local_ver'], $appinfo[0]['dbver'], '>') || $options['force-update-app']) {
                    app::get($app_id)->runtask('pre_update', array(
                        'dbver' => $appinfo[0]['dbver']
                    ));
                    kernel::single('base_application_manage')->update_app_content($app_id);
                    app::get($app_id)->runtask('post_update', array(
                        'dbver' => $appinfo[0]['dbver']
                    ));
                    app::get('base')->model('apps')->update(array(
                        'dbver' => $appinfo[0]['local_ver']
                    ), array(
                        'app_id' => $app_id
                    ));
                } else {
                    kernel::single('base_application_manage')->update_app_content($app_id);
                }
            }
            logger::info('Applications database and services is up-to-date, ok.');
        }
    }

    var $command_trace = '打开/关闭性能检测';

    function command_trace($mode = null) {
        switch ($mode) {
            case 'on':
                $this->register_trigger('trace');
                break;
            
            case 'off':
                $this->unregister_trigger('trace');
                break;
        }
        $this->shell->skip_trigger = true;
        echo 'Trace mode is ', $this->shell->trigger['trace'] ? 'on' : 'off';
    }

    var $command_status = '显示系统状态';

    function command_status($part = null) {
        $partlen = strlen($part);
        foreach (kernel::servicelist('status') as $srv) {
            foreach ($srv->get_status() as $k => $v) {
                if (! $partlen || substr($k, 0, $partlen + 1) == $part . '.') {
                    echo $k, '=>', $v, "\n";
                }
            }
        }
    }

    var $command_search = '在程序库中搜索';

    function command_search() {
        $keywrods = func_get_args();
        foreach ($keywrods as $word) {
            $where[] = "app_id like '%{$word}%' or app_name like '%{$word}%' or `description` like '%{$word}%'";
        }
        $sql = 'select app_id,app_name,description,local_ver,remote_ver from sdb_base_apps where 1 and ' . implode(' and ', $where);
        $rows = kernel::database()->select($sql);
        $this->output_table($rows);
    }

    function begin_trace() {
        $this->memtrace_begin = memory_get_usage();
        list ($usec, $sec) = explode(" ", microtime());
        $this->time_start = ((float) $usec + (float) $sec);
    }

    function end_trace() {
        if (! $this->memtrace_begin)
            return;
        list ($usec, $sec) = explode(" ", microtime());
        $time_start = ((float) $usec + (float) $sec);
        $mem = memory_get_usage() - $this->memtrace_begin;
        
        list ($usec, $sec) = explode(" ", microtime());
        $timediff = ((float) $usec + (float) $sec) - $this->time_start;
        printf("\n * Command memroy useage = %d, Time left = %f ", $mem, $timediff);
    }

    public $command_createproject = '创建新项目';

    function command_createproject($project_path = null, $install_confirm = null) {
        if (! $project_path) {
            $project_path = readline('Project path: ');
        }
        
        while (file_exists($project_path)) {
            $project_path = readline('Project already exists. enter anthoer one: ');
        }
        
        $project_name = basename($project_path);
        
        // init files
        $base_dir = dirname(__FILE__) . '/../../';
        logger::info('Init project... ' . realpath($project_path . '/' . $project_name));
        utils::cp($base_dir . '/examples/project', $project_path);
        utils::cp($base_dir, $project_path . '/app/base');
        utils::cp($base_dir . '/examples/app', $project_path . '/app/' . $project_name);
        chmod($project_path . '/app/base/cmd', 0744);
        chmod($project_path . '/data', 0777);
        utils::replace_p($project_path . '/config', array(
            '' => $project_name
        ));
        utils::replace_p($project_path . '/app/' . $project_name, array(
            '' => $project_name
        ));
        
        logger::info('Init project ok.');
        
        if ($install_confirm === null) {
            do {
                $install_confirm = readline('Install now? [Y/n] ');
                switch (strtolower(trim($install_confirm))) {
                    case '':
                    case 'y':
                        $install_confirm = true;
                        $command_succ = true;
                        break;
                    
                    case 'n':
                        $install_confirm = false;
                        $command_succ = true;
                        break;
                    
                    default:
                        $command_succ = false;
                }
            } while (! $command_succ);
        }
        
        $install_command = 'app' . DIRECTORY_SEPARATOR . 'base' . DIRECTORY_SEPARATOR . 'cmd install ' . $project_name;
        
        if ($install_confirm) {
            logger::info('Installing...');
            logger::info("\n" . $project_path . ' > ' . $install_command . "\n");
            chdir($project_path);
            passthru($install_command);
        } else {
            "Change dir to $project_dir: " . $install_command;
        }
    }

    var $command_kvrecovery = 'kvstore数据恢复';

    function command_kvstorerecovery($instance = null) {
        return $this->command_kvrecovery($instance);
    }

    function command_kvrecovery($instance = null) {
        if (! is_null($instance) && ! defined('FORCE_KVSTORE_STORAGE')) {
            $instance = trim($instance);
            if (! (strpos($instance, '_') === 0)) {
                $instance = 'base_kvstore_' . $instance;
            }
            define('FORCE_KVSTORE_STORAGE', $instance);
        }
        base_kvstore::config_persistent(false);
        $testObj = base_kvstore::instance('test');
        if (get_class($testObj->get_controller()) === 'base_kvstore_mysql') {
            logger::info('The \'base_kvstore_mysql\' is default persistent, Not necessary recovery');
            exit();
        }
        logger::info('KVstore Recovery...');
        $db = kernel::database();
        $count = $db->count('SELECT count(*) AS count FROM sdb_base_kvstore', true);
        if (empty($count)) {
            logger::info('No data recovery');
            exit();
        }
        $pagesize = 100;
        $page = ceil($count / 100);
        for ($i = 0; $i < $page; $i++) {
            $rows = $db->selectlimit('SELECT * FROM sdb_base_kvstore', $pagesize, $i * $pagesize);
            foreach ($rows as $row) {
                // logger::info($row['key']);continue;
                $row['value'] = unserialize($row['value']); // todo:合法数据
                if (base_kvstore::instance($row['prefix'])->recovery($row)) {
                    logger::info($row['prefix'] . '=>' . $row['key'] . ' ... Recovery Success');
                } else {
                    logger::info($row['prefix'] . '=>' . $row['key'] . ' ... Recovery Failure');
                }
            }
        }
    }

    // End Function
    var $command_kvdelexpires = 'kvstore清除过期数据，开启持久化功能有效';

    function command_kvdelexpires() {
        logger::info('KVstore Delete Expires Data...');
        base_kvstore::delete_expire_data();
    }

    // End Function
    var $command_cacheclean = '清除缓存';

    function command_cacheclean() {
        logger::info('Cache Clear...');
        cachemgr::init(true);
        if (cachemgr::clean($msg)) {
            logger::info($msg ? $msg : '...Clear Success');
        } else {
            logger::info($msg ? $msg : '...Clear Failure');
        }
        cachemgr::init(false);
    }

    // End Function
    var $command_configcompat = 'config兼容配置检测';

    function command_configcompat() {
        kernel::single('base_setup_config')->write_compat();
    }

    // End Function
    var $command_crontab = '运行计划任务';

    function command_crontab() {
        ob_start();
        base_crontab_schedule::trigger_all();
        logger::info('此命令只作为临时处理方案. 如果希望crontab触发, 请用script/crontab/crontab.php');
    }

    // End Fun
    var $command_crontablist = '列出当前所有crontab';

    function command_crontablist() {
        foreach (app::get('base')->model('crontab')->getlist('id, description, schedule') as $row) {
            logger::info($row['id'] . ' : ' . $row['description'] . ' : ' . $row['schedule']);
        }
    }

    var $command_crontabexec = '执行指定计划任务，任务名需是crontablist里列出来的';

    function command_crontabexec() {
        $crontab = func_get_args();
        
        if (count($crontab) !== 1) {
            logger::info('usage: php cmd crontabexec <crontab_name>');
            exit();
        }
        
        $cron_id = $crontab[0];
        if (base_crontab_schedule::is_valid_cronentry($cron_id) === false) {
            logger::info('cron: ' . $cron_id . ' not fond');
            exit();
        }
        
        base_crontab_schedule::run_task($cron_id);
        exit();
    }
}

